﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DiscoveryLogic.Common.Numeric
{
    /// <summary> Uniform distribution; <A HREF="http://www.cern.ch/RD11/rkb/AN16pp/node292.html#SECTION0002920000000000000000"> Math definition</A>
    /// and <A HREF="http://www.statsoft.com/textbook/glosu.html#Uniform Distribution"> animated definition</A>.
    /// <p>
    /// Instance methods operate on a user supplied uniform random number generator; they are unsynchronized.
    /// <dt>
    /// Static methods operate on a default uniform random number generator; they are synchronized.
    /// <p>
    /// </summary>
    /// <author>  wolfgang.hoschek@cern.ch
    /// </author>
    /// <version>  1.0, 09/24/99
    /// </version>
    [Serializable]
    public class Uniform : AbstractContinousDistribution
    {
        protected internal double min;
        protected internal double max;

        // The uniform random number generated shared by all <b>static</b> methods. 
        protected internal static Uniform shared = new Uniform(makeDefaultGenerator());
        /// <summary> Constructs a uniform distribution with the given minimum and maximum, using a {@link cern.jet.random.engine.MersenneTwister} seeded with the given seed.</summary>
        public Uniform(double min, double max, int seed)
            : this(min, max, new MersenneTwister(seed))
        {
        }
        /// <summary> Constructs a uniform distribution with the given minimum and maximum.</summary>
        public Uniform(double min, double max, RandomEngine randomGenerator)
        {
            setRandomGenerator(randomGenerator);
            setState(min, max);
        }
        /// <summary> Constructs a uniform distribution with <tt>min=0.0</tt> and <tt>max=1.0</tt>.</summary>
        public Uniform(RandomEngine randomGenerator)
            : this(0, 1, randomGenerator)
        {
        }
        /// <summary> Returns the cumulative distribution function (assuming a continous uniform distribution).</summary>
        public virtual double cdf(double x)
        {
            if (x <= min)
                return 0.0;
            if (x >= max)
                return 1.0;
            return (x - min) / (max - min);
        }
        /// <summary> Returns a uniformly distributed random <tt>boolean</tt>.</summary>
        public virtual bool nextBoolean()
        {
            return randomGenerator.raw() > 0.5;
        }
        /// <summary> Returns a uniformly distributed random number in the open interval <tt>(min,max)</tt> (excluding <tt>min</tt> and <tt>max</tt>).</summary>
        public override double nextDouble()
        {
            return min + (max - min) * randomGenerator.raw();
        }
        /// <summary> Returns a uniformly distributed random number in the open interval <tt>(from,to)</tt> (excluding <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public virtual double nextDoubleFromTo(double from, double to)
        {
            return from + (to - from) * randomGenerator.raw();
        }
        /// <summary> Returns a uniformly distributed random number in the open interval <tt>(from,to)</tt> (excluding <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public virtual float nextFloatFromTo(float from, float to)
        {
            //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
            return (float)nextDoubleFromTo(from, to);
        }
        /// <summary> Returns a uniformly distributed random number in the closed interval <tt>[min,max]</tt> (including <tt>min</tt> and <tt>max</tt>).</summary>
        public override int nextInt()
        {
            //UPGRADE_TODO: Method 'java.lang.Math.round' was converted to 'System.Math.Round' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangMathround_double'"
            return nextIntFromTo((int)System.Math.Round(min), (int)System.Math.Round(max));
        }
        /// <summary> Returns a uniformly distributed random number in the closed interval <tt>[from,to]</tt> (including <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public virtual int nextIntFromTo(int from, int to)
        {
            //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
            return (int)((long)from + (long)((1L + (long)to - (long)from) * randomGenerator.raw()));
        }
        /// <summary> Returns a uniformly distributed random number in the closed interval <tt>[from,to]</tt> (including <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public virtual long nextLongFromTo(long from, long to)
        {
            /* Doing the thing turns out to be more tricky than expected.
            avoids overflows and underflows.
            treats cases like from=-1, to=1 and the like right.
            the following code would NOT solve the problem: return (long) (Doubles.randomFromTo(from,to));
			
            rounding avoids the unsymmetric behaviour of casts from double to long: (long) -0.7 = 0, (long) 0.7 = 0.
            checking for overflows and underflows is also necessary.
            */

            // first the most likely and also the fastest case.
            if (from >= 0 && to < System.Int64.MaxValue)
            {
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                return from + (long)(nextDoubleFromTo(0.0, to - from + 1));
            }

            // would we get a numeric overflow?
            // if not, we can still handle the case rather efficient.
            //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
            double diff = ((double)to) - (double)from + 1.0;
            if (diff <= System.Int64.MaxValue)
            {
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                return from + (long)(nextDoubleFromTo(0.0, diff));
            }

            // now the pathologic boundary cases.
            // they are handled rather slow.
            long random;
            if (from == System.Int64.MinValue)
            {
                if (to == System.Int64.MaxValue)
                {
                    //return Math.round(nextDoubleFromTo(from,to));
                    int i1 = nextIntFromTo(System.Int32.MinValue, System.Int32.MaxValue);
                    int i2 = nextIntFromTo(System.Int32.MinValue, System.Int32.MaxValue);
                    return ((i1 & unchecked((int)0xFFFFFFFFL)) << 32) | (i2 & unchecked((int)0xFFFFFFFFL));
                }
                //UPGRADE_TODO: Method 'java.lang.Math.round' was converted to 'System.Math.Round' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangMathround_double'"
                random = (long)System.Math.Round(nextDoubleFromTo(from, to + 1));
                if (random > to)
                    random = from;
            }
            else
            {
                //UPGRADE_TODO: Method 'java.lang.Math.round' was converted to 'System.Math.Round' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangMathround_double'"
                random = (long)System.Math.Round(nextDoubleFromTo(from - 1, to));
                if (random < from)
                    random = to;
            }
            return random;
        }
        /// <summary> Returns the probability distribution function (assuming a continous uniform distribution).</summary>
        public virtual double pdf(double x)
        {
            if (x <= min || x >= max)
                return 0.0;
            return 1.0 / (max - min);
        }
        /// <summary> Sets the internal state.</summary>
        public virtual void setState(double min, double max)
        {
            if (max < min)
            {
                setState(max, min); return;
            }
            this.min = min;
            this.max = max;
        }
        /// <summary> Returns a uniformly distributed random <tt>boolean</tt>.</summary>
        public static bool staticNextBoolean()
        {
            lock (shared)
            {
                return shared.nextBoolean();
            }
        }
        /// <summary> Returns a uniformly distributed random number in the open interval <tt>(0,1)</tt> (excluding <tt>0</tt> and <tt>1</tt>).</summary>
        public static double staticNextDouble()
        {
            lock (shared)
            {
                return shared.nextDouble();
            }
        }
        /// <summary> Returns a uniformly distributed random number in the open interval <tt>(from,to)</tt> (excluding <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public static double staticNextDoubleFromTo(double from, double to)
        {
            lock (shared)
            {
                return shared.nextDoubleFromTo(from, to);
            }
        }
        /// <summary> Returns a uniformly distributed random number in the open interval <tt>(from,to)</tt> (excluding <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public static float staticNextFloatFromTo(float from, float to)
        {
            lock (shared)
            {
                return shared.nextFloatFromTo(from, to);
            }
        }
        /// <summary> Returns a uniformly distributed random number in the closed interval <tt>[from,to]</tt> (including <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public static int staticNextIntFromTo(int from, int to)
        {
            lock (shared)
            {
                return shared.nextIntFromTo(from, to);
            }
        }
        /// <summary> Returns a uniformly distributed random number in the closed interval <tt>[from,to]</tt> (including <tt>from</tt> and <tt>to</tt>).
        /// Pre conditions: <tt>from &lt;= to</tt>.
        /// </summary>
        public static long staticNextLongFromTo(long from, long to)
        {
            lock (shared)
            {
                return shared.nextLongFromTo(from, to);
            }
        }
        /// <summary> Sets the uniform random number generation engine shared by all <b>static</b> methods.</summary>
        /// <param name="randomGenerator">the new uniform random number generation engine to be shared.
        /// </param>
        public static void staticSetRandomEngine(RandomEngine randomGenerator)
        {
            lock (shared)
            {
                shared.setRandomGenerator(randomGenerator);
            }
        }
        /// <summary> Returns a String representation of the receiver.</summary>
        public override System.String ToString()
        {
            return this.GetType().FullName + "(" + min + "," + max + ")";
        }
    }
}
